unit uMainFrm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,
  GlScene, GlObjects, GlCadencer, GLWin32Viewer, GLRenderContextInfo, GlWindowsFont, GlHudObjects,
  VectorGeometry, GlMaterial,

  uSplineNavigator;
const
  ImagesFolder = 'SplineNavigator';
const
  PortsCount = 5;
  PortNames: array [1..PortsCount] of String = ('St. Petersburg', 'Amsterdam', 'New York', 'Port Nassau', 'Tokyo');
  NamesPos : array [1..PortsCount] of TVector = ((241, 74, 0, 1), (725, 470, 0, 1), (581, 272, 0, 1), (253, 265, 0, 1), (255, 438, 0, 1));
  PortPos  : array [1..PortsCount] of TVector = ((332, 82, 0, 1), (782, 466, 0, 1), (577, 294, 0, 1), (319, 258, 0, 1), (300, 464, 0, 1));
type
  TfrmMain = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    {Main Objects}
    fGlScene  : TGlScene;
    fCadencer : TGLCadencer;
    fViewer   : TGLSceneViewer;
    fCamera   : TGlCamera;
    fMainDummy: TGlDummyCube;
    fRenderer : TGLDirectOpenGL;
    fMatLib   : TGLMaterialLibrary;
    fFpsTimer : Single;
    fMousePressed: Boolean;

    {Text Objects}
    fWinFont: TGLWindowsBitmapFont;
    fHudText: TGLHUDText;
    fFPSText: TGLHUDText;

    {Navigator Objects}
    fNavigator: TSplineNavigator;
    fTime: Single;
    fDuration: Single;

    {Graphics Objects}
    fBack: TGLHudSprite;
    fShip: TGLHudSprite;
    fForwardDir: Boolean;
    fShiping: Boolean;

    {PortName animation}
    fPortName: TGLHUDText;
    fPortSelected: Integer;
    fPortAnimate: Integer;
    fAnimationTime: Single;
    fAnimationDuration: Single;
    fAnimateShow: Boolean;
  public
    Procedure OnCadencerProgress(Sender : TObject; const deltaTime, newTime : Double);
    Procedure OnDirectRender(Sender: TObject; var rci: TRenderContextInfo);
    Procedure StartShip(aTradePath: Integer);
    Procedure CheckPortsForSelect;
    Procedure InitFont;
    Procedure InitMaterials;
    Procedure InitNavigatorDemo;
  end;

var
  frmMain: TfrmMain;

implementation
uses
  GlTexture, GlCompositeImage, GlFilePng, GlFileJPEG, GlKeyboard,
  uDemoInit;

  {$R *.dfm}
//------------------------------------------------------------------------------
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  fFpsTimer := 0;
  CreateDemo(self, fGlScene, fCadencer, fViewer);
  InitDemo(fGlScene, fCamera, fMainDummy, fRenderer);
  fViewer.Camera := fCamera;
  fCadencer.OnProgress := OnCadencerProgress;
  fRenderer.OnRender := OnDirectRender;
  fCadencer.Enabled := true;

  InitFont;

  InitMaterials;
  InitNavigatorDemo;
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.InitFont;
begin
  fWinFont := TGLWindowsBitmapFont.Create(self);
  fWinFont.Font.Name := 'Arial';
  fWinFont.Font.Size := 8;
  fHudText := TGLHUDText.CreateAsChild(fMainDummy);
  with fHudText do
  begin
    BitmapFont := fWinFont;
    Text := 'Lampogolovii presents...' + #13#10 +
            'SplineNavigator Demo' + #13#10 + 
            'Controls:  Mouse Click on port' + #13#10 +
            'Space:  for render Control Points';
    Position.SetPoint(790, 3, 0);
    Alignment := taRightJustify;
  end;

  fFPSText := TGLHUDText.CreateAsChild(fMainDummy);
  fPortName := TGLHUDText.CreateAsChild(fRenderer);
  with fFPSText do
  begin
    BitmapFont := fWinFont;
    Position.SetPoint(790, 80, 0);
    Alignment := taRightJustify;
  end;

  fPortName.ModulateColor.SetColor(0, 0, 0, 1);
  with fPortName do
  begin
    BitmapFont := fWinFont;
    Alignment := taLeftJustify;
    Visible := false;
  end;
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.InitMaterials;
  Procedure AddMaterial(fFileName, fMaterialName: String);
  begin
    with fMatLib.Materials.Add do
    begin
      with Material do
      begin
        MaterialOptions := [moIgnoreFog, moNoLighting];
        Texture.Disabled := false;
        BlendingMode := bmTransparency;
        Texture.TextureMode := tmModulate;
        with FrontProperties do
        begin
          Ambient. SetColor(1,1,1, 1);
          Diffuse. SetColor(1,1,1, 1);
          Emission.SetColor(1,1,1, 1);
          Specular.SetColor(1,1,1, 1);
        end;
        Texture.ImageClassName := 'TGLCompositeImage';
        TGLCompositeImage(Texture.Image).LoadFromFile(str_ImagesFolder + '\' + ImagesFolder + '\' + fFileName);
      end;
      Name := fMaterialName;
    end;
  end;
begin
  fMatLib := TGLMaterialLibrary.Create(Self);
  AddMaterial('ship_1.png', 'ship');
  AddMaterial('back_1.jpg', 'back');
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.InitNavigatorDemo;
begin
  fNavigator := TSplineNavigator.Create;
  fShiping := false;
  fTime := 0;
  fDuration := 6;
  fAnimationDuration := 0.4;

  fShip := TGLHudSprite.CreateAsChild(fRenderer);
  fBack := TGLHudSprite.CreateAsChild(fRenderer);
  with fShip do
  begin
    Width := 32;
    Height := 32;
    Material.MaterialLibrary := fMatLib;
    Material.LibMaterialName := 'ship';
    Visible := false;
  end;

  with fBack do
  begin
    Position.SetPoint(400, 300, 0);
    Width := 800;
    Height := 600;
    Material.MaterialLibrary := fMatLib;
    Material.LibMaterialName := 'back';
    Visible := false;
  end;
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.OnCadencerProgress(Sender : TObject; const deltaTime, newTime : Double);
var
  StrFps: string;
begin
  fViewer.Invalidate;
  fFpsTimer := fFpsTimer + DeltaTime;
  if fFpsTimer > 1 then
  begin
    Str(fViewer.Buffer.FramesPerSecond:0:2, StrFps);
    fFpsText.Text := 'FPS: ' + StrFps;
    fViewer.Buffer.ResetPerformanceMonitor;
    fFpsTimer := 0;
  end;

  if not fMousePressed and IsKeyDown(VK_LBUTTON) and (fPortSelected > -1) and (fShiping = false) then
    StartShip(fPortSelected);
  fMousePressed := IsKeyDown(VK_LBUTTON);

  CheckPortsForSelect;

  if fPortAnimate > -1 then
  begin
    if fAnimateShow then
    begin
      fAnimationTime := fAnimationTime + DeltaTime;
      if fAnimationTime >= fAnimationDuration then
        fAnimationTime := fAnimationDuration;
    end
    else
    begin
      fAnimationTime := fAnimationTime - DeltaTime;
      if fAnimationTime < 0 then
      begin
        fAnimationTime := 0;
        fPortAnimate := -1;
      end;
    end;
  end;

  if fForwardDir then
  begin
    fTime := fTime + DeltaTime;
    if fTime > fDuration then
    begin
      fTime := fDuration;
      fForwardDir := false;
    end;
  end
  else
  begin
    fTime := fTime - DeltaTime;
    if fTime < 0 then
    begin
      fTime := 0;
      fForwardDir := true;
      fShiping := false;
    end;
  end;
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.OnDirectRender(Sender: TObject; var rci: TRenderContextInfo);
var
  Dir, Pos: TVector;
  i: Integer;
begin
  fBack.Render(rci);

  if fShiping and IsKeyDown(' ') then
    for i := 1 to fNavigator.BasePointsCount - 1 do
    begin
      fShip.Position.SetPoint(fNavigator.GetBasePointByIndex(i));
      Dir := VectorNormalize(fNavigator.GetSlopeByUnit(i / (fNavigator.BasePointsCount - 1)));
      if not fForwardDir then
        fShip.Rotation := ArcTan2(Dir[1], -Dir[0]) * 180 / pi
      else
        fShip.Rotation := ArcTan2(-Dir[1], Dir[0]) * 180 / pi;
      fShip.Render(rci);
    end;


  if fShiping then
  begin
    Pos := fNavigator.GetPointByUnit(fTime / fDuration);
    Dir := VectorNormalize(fNavigator.GetSlopeByUnit(fTime / fDuration));

    fShip.Position.SetPoint(Pos);
    if not fForwardDir then
      fShip.Rotation := ArcTan2(Dir[1], -Dir[0]) * 180 / pi
    else
      fShip.Rotation := ArcTan2(-Dir[1], Dir[0]) * 180 / pi;
    fShip.Render(rci);
  end;

  for i := 1 to PortsCount do
    if (fPortSelected = i) or (fPortAnimate = i)then
    begin
      fPortName.Text := PortNames[i];
      fPortName.ModulateColor.SetColor(0, 0, 0, fAnimationTime / fAnimationDuration);
      Pos := NamesPos[i];
      Pos[1] := Pos[1] + 20 * (-1 + sin(fAnimationTime / fAnimationDuration * pi /2));
      fPortName.Position.SetPoint(Pos);
      fPortName.Render(rci);
    end;
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.StartShip(aTradePath: Integer);
begin
  fForwardDir := true;
  fTime := 0;
  fShiping := true;
  fNavigator.DeletePoints;

  with fNavigator do
    case aTradePath of
      1:begin
          Add2d(0, 600);    Add2d(100, 589);  Add2d(175, 574);  Add2d(275, 544);
          Add2d(343, 503);  Add2d(403, 443);  Add2d(441, 362);  Add2d(443, 272);
          Add2d(413, 189);  Add2d(371, 124);  Add2d(332, 85);
        end;
      2:begin
          Add2d(0, 600);    Add2d(155, 559);  Add2d(263, 507);  Add2d(376, 471);
          Add2d(459, 513);  Add2d(558, 573);  Add2d(679, 530);  Add2d(722, 447);  Add2d(778, 461);
        end;
      3:begin
          Add2d(0, 600);    Add2d(139, 559);  Add2d(277, 520);  Add2d(364, 576);
          Add2d(486, 545);  Add2d(518, 447);  Add2d(463, 376);  Add2d(471, 279);  Add2d(574, 293);
        end;
      4:begin
          Add2d(0, 600);    Add2d(121, 568);  Add2d(230, 517);  Add2d(339, 523);
          Add2d(425, 582);  Add2d(485, 510);  Add2d(492, 415);  Add2d(448, 334);
          Add2d(498, 253);  Add2d(438, 200);  Add2d(351, 174);  Add2d(320, 255);
        end;
      5:begin
          Add2d(0, 600);    Add2d(98, 567);  Add2d(205, 565);  Add2d(302, 573);
          Add2d(345, 495);  Add2d(354, 399);  Add2d(427, 355);  Add2d(515, 431);
          Add2d(488, 512);  Add2d(422, 563);  Add2d(345, 526);  Add2d(300, 463);
        end;
    end;
  fNavigator.CreateCubicSpline;
end;
//------------------------------------------------------------------------------
Procedure TfrmMain.CheckPortsForSelect;
var
  i: Integer;
  MPos: TPoint;
  dx, dy: integer;
begin
  dx := 20;
  dy := 20;
  GetCursorPos(MPos);
  MPos := fViewer.ScreenToClient(MPos);

  fPortSelected := -1;
  for i := 1 to PortsCount do
    if (MPos.x > PortPos[i][0] - dx) and (MPos.x < PortPos[i][0] + dx)
    and(MPos.y > PortPos[i][1] - dy) and (MPos.y < PortPos[i][1] + dy)then
    begin
      fPortSelected := i;
      fPortAnimate := i;
      if fPortSelected <> i then
        fAnimationTime := 0;
      fAnimateShow := true;
      break;
    end;

  if fAnimateShow and (fPortSelected = -1) then
    fAnimateShow := false;
end;
//------------------------------------------------------------------------------
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  FreeAndNil(fNavigator);
end;

end.
